package Recomendadores.Semantico;
import java.util.ArrayList;
import java.util.Iterator;

import UserProfile.Configuration;
import UserProfile.ConnectionDB;

import com.hp.hpl.jena.ontology.DatatypeProperty;
import com.hp.hpl.jena.ontology.Individual;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;

public class Inferencia {
	
	/**Metodo encargado de inferir el DOI de un contenido audiovisual para un usuario dado, 
	 * empleando las secuancias de propiedades relevantes para el mismo y utilizando las asociaciones rho-path y rho-join
	 *  
	 * @param user perfil-ontologia del usuario, de donde se extraeran las secuencias de propiedades
	 * @param filtradas lista de secuencias de propiedades relevantes para el usuario
	 * @param movie contenido al que se quiere inferir el DOI
	 * @param idUser id del usuario en cuestion
	 * @param conf configuracion del sistema
	 * @return valor del DOI inferido	 
	 */
	public static float inferedDOI(OntModel user,ArrayList<PropertySequence> filtradas, Individual movie, int idUser, Configuration conf, boolean usuario) {

		//obtengo las asociaciones rho-path de las secuencias de propiedades filtradas		
		ArrayList<PropertySequence> asociacionesRhoPath = rhoPath(filtradas);

		//inicializo variables para el promedio ponderado
		float numerador = 0;
		float denominador =0;

		//recorro las asociaciones		
		Iterator<PropertySequence> i = asociacionesRhoPath.iterator();
		while(i.hasNext()){
			PropertySequence sequence = i.next();
			float largo = sequence.length(); //obtengo el largo de la secuencia
			Individual peliculaFinal = sequence.getNode(sequence.length()-1); //pelicula que es el final de la secuencia					

			//obtengo la propiededad hasDOI de la pelicula final y agrego la informacion a la ecuacion
			DatatypeProperty hasDOI = user.getDatatypeProperty(conf.getNS()+"hasDOI");			
                        RDFNode prop = peliculaFinal.getPropertyValue(hasDOI); 
                       // System.out.println(peliculaFinal.toString());
			if (prop !=null){
                                
				float doi = prop.asLiteral().getInt();
				float aux = doi/largo;
				numerador = numerador+aux;
				float aux2 = 1/largo;
				denominador = denominador+aux2;
			}
		}

		//obtengo el rho-join, que es el aporte de los generos de la pelicula
		float join = rhoJoin(movie, idUser, conf,usuario);	
		if(join>0){
			//el largo de una asociacion rho-join es de 3, porque son asociaciones de una sola propiedad
			float largoGeneros = 3;
                        float aux = join/largoGeneros;
			numerador = numerador+aux;
			float aux2 = 1/largoGeneros;
			denominador = denominador+aux2;
		}
		
		float[] generos = rhoJoin2(movie, idUser, conf,usuario);
		float largoGeneros = 3;
		for(int j=0;j<generos.length;j++){
			float aux = generos[j]/largoGeneros;
			numerador = numerador+aux;
			float aux2 = 1/largoGeneros;
			denominador = denominador+aux2;
		}

		if(numerador>0 && denominador>0){
			//el resultado final es un promedio ponderado de las preferencias que se relacionan a travez de los phipath con la movie a inferir
			return numerador/denominador;	
		}else{			
			return 0;			
		}
	}

	/**Metodo encargado de obtener las asociaciones rho-join, que es el aporte de los generos de la pelicula
	 * 
	 * @param movie contenido al que se quiere inferir el DOI
	 * @param idUser id del usuario en cuestion
	 * @param conf configuracion del sistema
	 * @return valor de los rho-join	 
	 */
	public static float rhoJoin(Individual movie, int idUser, Configuration conf, boolean usuario){
		//se obtienen las clases padre de la pelicula
		ExtendedIterator<OntClass> i = movie.listOntClasses(true);

		//inicializo las variables para hallar el promedio
		float doi=0;
		int count = 0;
		boolean five = false;
		boolean one = false;
		while (i.hasNext()){
			OntClass clase = i.next();
                        String tabla=clase.getLocalName();
			float result = ConnectionDB.select(conf.getURL(), conf.getDBNAME(), conf.getDBUSER(), conf.getDBPASS(), tabla, idUser); 
			if(result >= 4.5) five = true;
			if(result <= 1.5) one = true;
			doi = doi + result;
			count++;
		}

		if(five && !one){
			return 5;
		}else if (one && !five){
			return 1;
		}else{
			return doi/count;
		}
	}
	
	public static float[] rhoJoin2(Individual movie, int idUser, Configuration conf,boolean usuario){
		//se obtienen las clases padre de la pelicula
		ExtendedIterator<OntClass> i = movie.listOntClasses(true);
		
		ArrayList<Float> lista = new ArrayList<Float>();
		while (i.hasNext()){
			OntClass clase = i.next();
			String tabla = clase.getLocalName();
			float result = ConnectionDB.select(conf.getURL(), conf.getDBNAME(), conf.getDBUSER(), conf.getDBPASS(), tabla, idUser); 
			lista.add(result);
		}
		
		float[] lista2 = new float[lista.size()];
		for(int j=0;j<lista.size();j++){
			lista2[j] = lista.get(j).floatValue();
		}
		
		return lista2;		
	}


	/**Encuentra las asociaciones rho-path de las secuencias de propiedades
	 *  
	 * @param filtradas lista de secuencias de propiedades relevantes para el usuario
	 * @return asociaciones rho-path	 
	 */
	private static ArrayList<PropertySequence> rhoPath(ArrayList<PropertySequence> filtradas) {
		ArrayList<PropertySequence> asociaciones = new ArrayList<PropertySequence>();
		Iterator<PropertySequence> i = filtradas.iterator();
		//recorro las secuencias de propiedades
		while(i.hasNext()){
			PropertySequence sequence = i.next();
			ArrayList<Individual> arrayRes = sequence.getSequence(); //obtengo la secuencia de instancias
			Iterator<Individual> j = arrayRes.iterator();
			//recorro la secuencia en busca de asociaciones rho-path
			int count = 1;//creo un contador para evitar tomar la primer pelicual como asociacion rho-path
			while(j.hasNext()){
				Individual individual = j.next();
				OntClass clase = individual.getOntClass();	//obtengo la clase padre de la instancia
				OntClass superclass = clase.getSuperClass();
				if(superclass.getLocalName().equals("ContentCS")){
					//si no es la primer pelicula agrego la asociacion
					if(count!=1){
						Individual aux = individual;
						//obtengo la subsecuencia con final en la pelicula aux
						PropertySequence subsequence = sequence.getSubSequence(sequence.getIndex(aux));
						asociaciones.add(subsequence);
					}
					count++;
				}
			}		
		}
		
		//elimino las asociaciones repetidas, si es que las hay
		asociaciones = eliminarRepetidas(asociaciones);
		
		return asociaciones;		
	}

	/**Metodo encargado de eliminar asociaciones semanticas repetidas de una lista
	 *  
	 * @param asociaciones semanticas
	 * @return asociaciones seamnticas filtradas	 
	 */
	private static ArrayList<PropertySequence> eliminarRepetidas(ArrayList<PropertySequence> asociaciones) {
		ArrayList<PropertySequence> filtradas = new ArrayList<PropertySequence>();
		
		//recorro las asociaciones en busca de repetidas
		for(int i=0;i<asociaciones.size();i++){
			PropertySequence pivot = asociaciones.get(i);
			//busco si esta duplicada
			boolean sonIguales = false;
			for(int j=i+1;j<asociaciones.size();j++){				
				boolean equals = pivot.equals(asociaciones.get(j));
				if(equals) sonIguales=equals;				
			}
			//si NO esta duplicada la agrego
			if(!sonIguales) filtradas.add(pivot);
		}
		
		return filtradas;

	}

}